import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { ROLES_KEY } from '../decorators/roles.decorator'
import { Role } from '../enum/roles.enum'
import { UserService } from '../user/user.service'

export interface requertWithUser extends Request {
  user?: {
    userId?: number
    username?: string
  }
}

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private userService: UserService
  ) {}
  async canActivate(context: ExecutionContext): Promise<boolean> {
    /*  Reflector 用于获取存储在类或方法上的元数据。它是通过 @nestjs/core 提供的
Reflector提供 getAllAndMerge(指定元数据的键,[要读取元数据的目标对象数组])和getAllAndOverride
    getAllAndMerge: 用于读取多个源（方法和类）上的元数据并将它们合并，通常合并成一个数组
    getAllAndOverride：用于类和方法）中获取元数据，并用后来的源覆盖前面的源,适用于覆盖类级别的权限数据 */
    // 1.获取类和方法上元数据的角色信息
    const requiredRoles = this.reflector.getAllAndMerge<Role[]>(ROLES_KEY, [
      context.getHandler(), //获取当前处理方法的元数据
      context.getClass() //获取当前类的元数据
    ])
    // 2.获取用户的角色信息
    // 获取请求对象 switchToHttp():访问http请求和响应对象 getRequest:获取请求对象
    const req = context.switchToHttp().getRequest<requertWithUser>()
    if (req.user?.username) {
      const user = await this.userService.find(req.user?.username)
      // 获取user的角色id
      const roleIds = user?.roles.map((o) => o.id)
      // some: 用来测试数组中是否至少有一个元素满足指定的条件  includes：数组中是否包含某个元素
      //requiredRoles中的某个角色是否在roleIds中
      const flag = requiredRoles.some((role) => roleIds?.includes(role))
      return flag
    }
    return false //不处理该请求
  }
}
